/*
 * @Author: FBB
 * @Date: 2020-06-01 10:11:04
 * @LastEditors: FBB
 * @LastEditTime: 2020-06-01 14:03:12
 * @Description: 基于Promise A+规范实现Promise
 */

//2.1 promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(executor) {
  let that = this;
  that.status = PENDING;
  that.value = null;
  that.error = null;
  that.onFulfilledCallback = [];
  that.onRejectedCallback = [];

  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }

  function resolve(value) {
    setTimeout(() => {
      if (that.status === PENDING) {
        // 只能由pedning状态 => resolved状态 (避免调用多次resolve reject)
        that.status = FULFILLED;
        that.value = value;
        that.onFulfilledCallback.forEach((cb) => cb(that.value));
      }
    });
  }

  function reject(error) {
    setTimeout(() => {
      if (that.status === PENDING) {
        // 只能由pedning状态 => rejected状态 (避免调用多次resolve reject)
        that.status = REJECTED;
        that.error = error;
        that.onRejectedCallback.forEach((cb) => cb(that.error));
      }
    });
  }
}

/**
 * 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
 * @param  {promise} promise2 promise1.then方法返回的新的promise对象
 * @param  {[type]} x         promise1中onFulfilled的返回值
 * @param  {[type]} resolve   promise2的resolve方法
 * @param  {[type]} reject    promise2的reject方法
 */
function resolvePromise(promise, x, resolve, reject) {
  let called = false;
  let then = null;
  //2.3.1 处理循环引用
  if (promise === x) {
    reject(new TypeError());
  }
  //2.3.2 x为Promise
  else if (x instanceof MyPromise) {
    //2.3.2.1 x的status为pending，promise只需要保持为pending，直到x的状态为成功态或者失败态
    if (x.status === PENDING) {
      x.then(
        (res) => resolvePromise(promise, res, resolve, reject),
        (error) => reject(error)
      );
    } else {
      // x 已经处于执行态/拒绝态(值已经被解析为普通值)，用相同的值执行传递下去 promise
      x.then(resolve, reject);
    }
  }
  //2.3.3   x是一个对象或者函数
  else if ((x !== null && typeof x === "object") || typeof x === "function") {
    try {
      then = x.then; //2.3.3.1 赋值给then
    } catch (error) {
      reject(error);
    }
    //2.3.3.3 then是函数
    if (typeof then === "function") {
      try {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (e) {
        if (called) return;
        reject(e);
      }
    } else {
      //2.3.3.4 then不是函数，resolve
      resolve(x);
    }
  }
  // 2.3.4 x不是对象或函数
  else {
    resolve(x);
  }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  const that = this;
  let promise2 = null;
  //处理参数默认值，使得参数可以继续传递
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : () => that.value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : () => {
          throw that.error;
        };

  //成功态
  if (that.status === FULFILLED) {
    return (promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          let x = onFulfilled(that.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }));
  }

  //失败态
  if (that.status === REJECTED) {
    return (promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          onRejected(that.error);
        } catch (error) {
          reject(error);
        }
      });
    }));
  }

  //等待态
  if (that.status === PENDING) {
    return (promise2 = new Promise((resolve, reject) => {
      that.onFulfilledCallback.push((value) => {
        try {
          let x = onFulfilled(value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
      that.onRejectedCallback.push((error) => {
        try {
          let x = onRejected(error);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    }));
  }
};

//测试代码
MyPromise.deferred = function () {
  const defer = {};
  defer.promise = new MyPromise((resolve, reject) => {
    defer.resolve = resolve;
    defer.reject = reject;
  });
  return defer;
};

try {
  module.exports = MyPromise;
} catch (e) {}
